﻿namespace com.stifel.Extensions.SharePointExtensions
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;

    using com.stifel.Exceptions.SharePointExceptions;
    using com.stifel.SharePoint;

    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;
    using Microsoft.SharePoint.Navigation;

    public static class SPWebExtensions
    {
        #region Methods

        public static void ActivateFeature( this SPWeb spWeb, Guid featureId )
        {
            if ( featureId == null || featureId.Equals( Guid.Empty ) )
                throw new FeatureNotFoundException( );
            SPFeatureDefinition f = SPFarm.Local.GetFeatureDefinition( featureId );
            if ( f == null )
                throw new FeatureNotFoundException( featureId );
            if ( f.Scope == SPFeatureScope.Web )
            {
                try
                {
                    SPFeature spFeature = spWeb.Features.FirstOrDefault( a => a.DefinitionId.Equals( featureId ) );
                    if ( spFeature == null )
                    {
                        spWeb.Features.Add( featureId );
                        spWeb.Update( );
                    }
                }
                catch ( Exception e )
                {
                    throw new FeatureActivationException( featureId, f.GetTitle( CultureInfo.CurrentCulture ), spWeb.Url, e );
                }
            }
            else
                throw new FeatureActivationFailedNotScopedForWebException( featureId, f.GetTitle( CultureInfo.CurrentCulture ), spWeb.Url );
        }

        public static void AddGroupWithRole( this SPWeb spWeb, SPGroup spGroup, SPRoleType role )
        {
            SPRoleAssignment roleAssignment = new SPRoleAssignment( spGroup );
            SPRoleDefinition roleDefinition = spWeb.RoleDefinitions.GetByType( role );
            roleAssignment.RoleDefinitionBindings.Add( roleDefinition );
            spWeb.RoleAssignments.Add( roleAssignment );
            spWeb.Update( );
        }

        /// <summary>
        /// The specified SharePoint Security Group is added to all ancestors of this instance of SPWeb with the role of SPRoleType.Reader. The group is added to this instance of SPWeb with the specified role.
        /// </summary>
        /// <param name="spWeb"></param>
        /// <param name="spGroup"></param>
        /// <param name="role"></param>
        public static void AddGroupWithRoleUsingCascadingSecurityMatrix( this SPWeb spWeb, SPGroup spGroup, SPRoleType role )
        {
            bool ok = true;
            SPWeb p = spWeb;
            while ( ok )
            {
                using ( SPWeb o = p.ParentWeb )
                {
                    if ( o == null )
                    {
                        ok = false;
                        break;
                    }
                    else
                    {
                        o.AddGroupWithRole( spGroup, SPRoleType.Reader );
                        p = o;
                    }
                }
            }
            spWeb.AddGroupWithRole( spGroup, role );
        }

        public static void AddTopNavEntryToParent( this SPWeb spWeb )
        {
            SPNavigationNodeCollection topnav = spWeb.ParentWeb.Navigation.TopNavigationBar;
            SPNavigationNode n = topnav.Cast<SPNavigationNode>( ).FirstOrDefault( a => a.Url.Equals( spWeb.ServerRelativeUrl ) );
            if ( n == null )
            {
                n = new SPNavigationNode( spWeb.Title, spWeb.ServerRelativeUrl );
                topnav.AddAsLast( n );
            }
        }

        public static bool ContainsContentType( this SPWeb spWeb, string name )
        {
            foreach ( SPContentType o in spWeb.ContentTypes )
                if ( o.Name.Equals( name, StringComparison.OrdinalIgnoreCase ) )
                    return true;
            return false;
        }

        public static bool ContainsSubweb( this SPWeb spWeb, string webUrl )
        {
            using ( SPWeb childWeb = spWeb.Webs.FirstOrDefault( a => a.Name.Equals( webUrl ) ) )
            {
                return childWeb == null ? false : true;
            }
        }

        public static SPList CreateAssetLibrary( this SPWeb spWeb, string rootFolderName, string title, string description, bool replaceExisting )
        {
            SPList spList = spWeb.Lists.GetListByRootFolder( rootFolderName );
            if ( spList != null && replaceExisting )
                spWeb.Lists.Delete( spList.ID );
            else if ( spList != null )
                return spList;
            SPListTemplate spListTemplate = spWeb.ListTemplates[ "Asset Library" ];
            Guid g = spWeb.Lists.Add( rootFolderName, description, spListTemplate );
            spList = spWeb.Lists[ g ];
            spList.ContentTypesEnabled = true;
            spWeb.Update( );
            spList = spWeb.Lists[ g ];
            spList.Title = title;
            spList.Update( );
            spList = spWeb.Lists[ g ];
            return spList;
        }

        public static SPContentType CreateContentType( this SPWeb spWeb, SPContentType parentContentType, string group, string name, string description, bool replaceExisting, params SPField[ ] fields )
        {
            SPContentType spContentType = GetContentTypeByName( spWeb, name );
            if ( spContentType != null && replaceExisting )
                spWeb.ContentTypes.Delete( spContentType.Id );
            else if ( spContentType != null )
                return spContentType;
            spContentType = new SPContentType( parentContentType, spWeb.ContentTypes, name );
            spContentType.Description = description;
            spContentType.Group = group;
            if ( fields.Length > 0 )
                foreach ( SPField o in fields )
                {
                    if ( o != null )
                    {
                        SPFieldLink spFieldLink = new SPFieldLink( o );
                        spContentType.FieldLinks.Add( spFieldLink );
                    }
                }
            spWeb.ContentTypes.Add( spContentType );
            spWeb.Update( );
            spContentType = spWeb.ContentTypes[ name ];
            return spContentType;
        }

        public static SPContentType CreateContentType( this SPWeb spWeb, string contentTypeId, string group, string name, string description, bool replaceExisting, params SPField[ ] fields )
        {
            SPContentType spContentType = GetContentTypeByName( spWeb, name );
            if ( spContentType != null && replaceExisting )
                spWeb.ContentTypes.Delete( spContentType.Id );
            else if ( spContentType != null )
                return spContentType;
            SPContentTypeId id = new SPContentTypeId( contentTypeId );
            spContentType = new SPContentType( id, spWeb.ContentTypes, name );
            spContentType.Description = string.IsNullOrEmpty( description ) ? string.Empty : description.Trim( );
            spContentType.Group = group;
            if ( fields.Length > 0 )
                foreach ( SPField o in fields )
                {
                    if ( o != null )
                    {
                        SPFieldLink spFieldLink = new SPFieldLink( o );
                        spContentType.FieldLinks.Add( spFieldLink );
                    }
                }
            spWeb.ContentTypes.Add( spContentType );
            spWeb.Update( );
            spContentType = spWeb.ContentTypes[ name ];
            return spContentType;
        }

        public static SPList CreateGenericList( this SPWeb spWeb, string rootFolderName, string title, string description, bool replaceExisting )
        {
            SPList spList = spWeb.Lists.GetListByRootFolder( rootFolderName );
            if ( spList != null && replaceExisting )
                spWeb.Lists.Delete( spList.ID );
            else if ( spList != null )
                return spList;
            Guid g = spWeb.Lists.Add( rootFolderName, description, SPListTemplateType.GenericList );
            spList = spWeb.Lists[ g ];
            spList.ContentTypesEnabled = true;
            spWeb.Update( );
            spList = spWeb.Lists[ g ];
            spList.Title = title;
            spList.Update( );
            spList = spWeb.Lists[ g ];
            return spList;
        }

        public static void DeactivateFeature( this SPWeb spWeb, Guid featureId )
        {
            SPFeatureDefinition f = SPFarm.Local.GetFeatureDefinition( featureId );
            if ( f == null )
                throw new FeatureNotFoundException( featureId );
            else
            {
                if ( f.Scope == SPFeatureScope.Web )
                {
                    try
                    {
                        SPFeature spFeature = spWeb.Features.FirstOrDefault( a => a.DefinitionId.Equals( featureId ) );
                        if ( spFeature != null )
                            spWeb.Features.Remove( featureId );
                        spWeb.Update( );
                    }
                    catch ( Exception e )
                    {
                        throw new FeatureDeactivationException( featureId, f.GetTitle( CultureInfo.CurrentCulture ), spWeb.Url, e );
                    }
                }
                else
                    throw new FeatureDeactivationFailedNotScopedForWebException( featureId, f.GetTitle( CultureInfo.CurrentCulture ), spWeb.Url );
            }
        }

        public static bool DeleteContentType( this SPWeb spWeb, string name )
        {
            bool ok = true;
            if ( spWeb.ContainsContentType( name ) )
            {
                SPContentType o = spWeb.GetContentTypeByName( name );
                if ( o != null )
                {
                    IList<SPContentTypeUsage> usages = SPContentTypeUsage.GetUsages( o );
                    if ( usages.Count <= 0 )
                    {
                        spWeb.ContentTypes.Delete( o.Id );
                        spWeb.Update( );
                    }
                    else
                        ok = false;
                }
            }
            return ok;
        }

        public static bool DeleteList( this SPWeb spWeb, string rootFolderName, bool deleteWithItems )
        {
            bool ok = true;
            if ( spWeb.Lists.ContainsList( rootFolderName ) )
            {
                SPList spList = spWeb.Lists.GetListByRootFolder( rootFolderName );
                if ( spList.ItemCount == 0 || deleteWithItems )
                {
                    try
                    {
                        spWeb.Lists.Delete( spList.ID );
                        spWeb.Update( );
                    }
                    catch
                    {
                        ok = false;
                    }
                }
            }
            return ok;
        }

        /// <summary>
        /// Gets all users that belong to the site collection to which the SharePoint Web site is a part. Returns the users as a list of type SPUserInfoExpanded.
        /// </summary>
        /// <param name="spWeb"></param>
        /// <returns>Returns a list of type SPUserInfoExpanded.</returns>
        public static List<SPUserInfoExpanded> GetAllUsers( this SPWeb spWeb )
        {
            SPUserCollection users = spWeb.SiteUsers;
            List<SPUserInfoExpanded> list = new List<SPUserInfoExpanded>( );
            foreach ( SPUser user in users )
            {
                list.Add( new SPUserInfoExpanded( user.Name, user.LoginName, user.Email, user.ID, user.Sid, user.Notes ) );
            }
            return list;
        }

        public static SPContentType GetContentTypeByName( this SPWeb spWeb, string name )
        {
            SPContentType contentType = null;
            SPWeb o = spWeb;
            bool ok = false;
            while ( !ok )
            {
                using ( SPWeb p = o )
                {
                    o.Dispose( );
                    contentType = getContentTypeByName( p, name );
                    if ( contentType == null )
                    {
                        o = p.ParentWeb;
                        if ( o == null )
                            break;
                    }
                    else
                        break;
                }
            }
            return contentType;
        }

        static SPContentType getContentTypeByName( SPWeb spWeb, string name )
        {
            SPContentType contentType = null;
            foreach ( SPContentType o in spWeb.AvailableContentTypes )
                if ( o.Name.Equals( name, StringComparison.OrdinalIgnoreCase ) )
                {
                    contentType = o;
                    break;
                }
            return contentType;
        }

        /// <summary>
        /// Returns the field with the specified static name from the collection. The specified instance of SPWeb is examined first for the specified field. If not found, then the root Web site of the site collection is examined for the specified field. If not found at the site collection level, the Web site hierarchy of the specified instance of SPWeb is examined, i.e. the parent Web site is examined, if not found, then the parent of the parent Web site is examined. This continues until the field is found, or until the full hierarchy of the specified Web site is examined.
        /// Suggestion: Do not change the static name once the field is created.
        /// </summary>
        /// <param name="spWeb"></param>
        /// <param name="staticName"></param>
        /// <returns></returns>
        public static SPField GetFieldByStaticName( this SPWeb spWeb, string staticName )
        {
            SPField spField = null;
            if ( spWeb.Fields.ContainsFieldWithStaticName( staticName ) )
                spField = spWeb.Fields.GetFieldByInternalName( staticName );
            else if ( spWeb.Site.RootWeb.Fields.ContainsFieldWithStaticName( staticName ) )
                spField = spWeb.Site.RootWeb.Fields.GetFieldByInternalName( staticName );
            else
            {
                bool ok = true;
                SPWeb p = spWeb;
                while ( ok )
                {
                    using ( SPWeb o = p.ParentWeb )
                    {
                        if ( o == null )
                        {
                            ok = false;
                            break;
                        }
                        else
                        {
                            if ( o.Fields.ContainsFieldWithStaticName( staticName ) )
                            {
                                spField = o.Fields.GetFieldByInternalName( staticName );
                                break;
                            }
                            else
                                p = o;
                        }
                    }
                }
            }
            return spField;
        }

        public static SPGroup GetGroup( this SPWeb spWeb, string groupName )
        {
            return spWeb.Groups.Cast<SPGroup>( ).FirstOrDefault( a => a.Name.Equals( groupName, StringComparison.OrdinalIgnoreCase ) );
        }

        public static List<SPGroup> GetSiteGroupsWithRole( this SPWeb spWeb, SPRoleType role )
        {
            SPRoleDefinition roleDefinition = spWeb.RoleDefinitions.GetByType( role );
            List<SPRoleAssignment> l = spWeb.RoleAssignments.Cast<SPRoleAssignment>( ).ToList( ).FindAll( a => a.RoleDefinitionBindings.Contains( roleDefinition ) );
            List<SPGroup> groups = new List<SPGroup>( );
            foreach ( SPRoleAssignment roleAssignment in l )
            {
                SPGroup spGroup = roleAssignment.Member as SPGroup;
                if ( spGroup != null )
                    groups.Add( spGroup );
            }
            return groups;
        }

        /// <summary>
        /// Gets the specified user by login name. The user must belong to the site collection to which the SharePoint Web site is a part. Returns the user as an instance of  type SPUserInfoExpanded.
        /// </summary>
        /// <param name="spWeb"></param>
        /// <returns>Returns a list of type SPUserInfoExpanded.</returns>
        public static SPUserInfoExpanded GetUserByLoginName( this SPWeb spWeb, string loginName )
        {
            SPUserInfoExpanded u = null;
            SPUserCollection users = spWeb.SiteUsers;
            foreach ( SPUser user in users )
            {
                if ( user.LoginName.Equals( loginName, StringComparison.OrdinalIgnoreCase ) )
                {
                    u = new SPUserInfoExpanded
                    {
                        Email = user.Email,
                        Id = user.ID,
                        LoginName = user.LoginName,
                        Name = user.Name,
                        Notes = user.Notes,
                        Sid = user.Sid
                    };
                }
            }
            return u;
        }

        public static SPWeb GetWeb( this SPWeb spWeb, string webUrl )
        {
            return spWeb.Webs.Cast<SPWeb>( ).FirstOrDefault( a => a.Name.Equals( webUrl ) );
        }

        public static bool IsFeatureActivated( this SPWeb spWeb, Guid featureId )
        {
            SPFeature spFeature = spWeb.Features.FirstOrDefault( a => a.DefinitionId.Equals( featureId ) );
            return spFeature == null ? false : true;
        }

        public static bool IsFeatureScoped( this SPWeb spWeb, Guid featureId )
        {
            SPFeatureDefinition f = SPFarm.Local.GetFeatureDefinition( featureId );
            if ( f == null )
                return false;
            else
                return f.Scope == SPFeatureScope.Web;
        }

        public static void RemoveGroup( this SPWeb spWeb, string name )
        {
            SPGroup spGroup = spWeb.Groups.Cast<SPGroup>( ).ToList( ).FirstOrDefault( a => a.Name.Equals( name ) );
            if ( spGroup != null )
            {
                spWeb.Groups.Remove( name );
                spWeb.Update( );
            }
        }

        #endregion Methods
    }
}